﻿//========================================================= 
/**@file ICBC_CmdHelper.h
 * @brief ICBC外设盒子命令编辑器
 * 
 * @date 2017-05-17   16:45:47
 * @author Zhyioo 
 * @version 1.0
 */ 
//--------------------------------------------------------- 
#ifndef _LIBZHOUYB_ICBC_CMDHELPER_H_
#define _LIBZHOUYB_ICBC_CMDHELPER_H_
//--------------------------------------------------------- 
#include "../../include/Base.h"
#include "../../include/Extension.h"
//--------------------------------------------------------- 
namespace zhou_yb {
namespace application {
namespace icbc {
//--------------------------------------------------------- 
/// 发送包
struct ICBC_SendMessage
{
    string DevId;
    string Action;
    uint ActionType;
    uint ActionId;
    uint TimeoutMs;
    string Arguments;

    ICBC_SendMessage()
    {
        DevId = "";
        Action = "";
        ActionType = 0;
        ActionId = 0;
        TimeoutMs = 0;
        Arguments = "";
    }
};
//--------------------------------------------------------- 
/// 接收包
struct ICBC_RecvMessage
{
    string DevId;
    string Action;
    uint ActionType;
    uint Result;
    uint ActionId;
    string Arguments;

    ICBC_RecvMessage()
    {
        DevId = "";
        Action = "";
        ActionType = 0;
        Result = 0;
        ActionId = 0;
        Arguments = "";
    }
};
//--------------------------------------------------------- 
/// ICBC外设盒子指令交互适配器
struct ICBC_CmdHelper
{
    /// 判断指定的包长格式是否正确
    static DeviceError::ErrorCode IsMsgFormat(size_t CMD_MIN_LEN, const ByteArray& buff, ByteBuilder* pBuff = NULL)
    {
        if(buff.GetLength() < CMD_MIN_LEN)
            return DeviceError::ArgLengthErr;

        byte CMD_STX[] = { 0x10, 0x02 };
        const size_t CMD_STX_LEN = 2;

        byte CMD_ETX[] = { 0x10, 0x03 };
        const size_t CMD_ETX_LEN = 2;

        if(!StringConvert::StartWith(buff, ByteArray(CMD_STX, CMD_STX_LEN)))
            return DeviceError::ArgFormatErr;

        ByteBuilder lenBuff(4);
        lenBuff = buff.SubArray(2, 4);
        ByteConvert::Reverse(lenBuff);

        size_t len = 0;
        ByteConvert::ToObject<size_t>(lenBuff, len);
        // 数据还未收全
        if(len > (buff.GetLength() - 8))
            return DeviceError::ArgLengthErr;

        // Json数据长度
        size_t datalen = 0;
        lenBuff.Clear();
        lenBuff = buff.SubArray(CMD_MIN_LEN - 6, 4);
        ByteConvert::Reverse(lenBuff);
        ByteConvert::ToObject<size_t>(lenBuff, datalen);

        ByteArray etxBuff = buff.SubArray(CMD_MIN_LEN - 2 + datalen);
        if(!StringConvert::StartWith(etxBuff, ByteArray(CMD_ETX, CMD_ETX_LEN)))
            return DeviceError::ArgFormatErr;

        if(pBuff != NULL)
        {
            pBuff->Append(buff.SubArray(0, len + 8));
        }
        return DeviceError::Success;
    }
    /// 判断发送包格式是否正确
    static DeviceError::ErrorCode IsSendFormat(const ByteArray& buff, ByteBuilder* pBuff = NULL)
    {
        const size_t CMD_MIN_LEN = (2 + 4 + 40 + 40 + 2 + 8 + 4 + 4 + 2);
        return IsMsgFormat(CMD_MIN_LEN, buff, pBuff);
    }
    /// 判断接收包格式是否正确
    static DeviceError::ErrorCode IsRecvFormat(const ByteArray& buff, ByteBuilder* pBuff = NULL)
    {
        const size_t CMD_MIN_LEN = (2 + 4 + 40 + 40 + 2 + 4 + 4 + 4 + 2);
        return IsMsgFormat(CMD_MIN_LEN, buff, pBuff);
    }
    /// 按照格式接收数据
    static bool RecvByFormat(IInteractiveTrans& dev, ByteBuilder& emptyBuffer, ByteBuilder* pCachedBuff = NULL)
    {
        // 10 02 开头 + 2字节长度
        const size_t CMD_HEAD_LEN = 6;
        if(IInteractiveTransHelper::ReadAssert(dev, CMD_HEAD_LEN, emptyBuffer) != DevHelper::EnumSUCCESS)
            return false;

        byte CMD_STX[] = { 0x10, 0x02 };
        const size_t CMD_STX_LEN = 2;

        byte CMD_ETX[] = { 0x10, 0x03 };
        const size_t CMD_ETX_LEN = 2;

        if(!StringConvert::StartWith(emptyBuffer, ByteArray(CMD_STX, CMD_STX_LEN)))
            return false;

        ByteBuilder lenBuff(4);
        const size_t LEN_SIZE = 4;
        size_t cmdlen = 0;

        lenBuff = emptyBuffer.SubArray(CMD_STX_LEN, LEN_SIZE);
        ByteConvert::Reverse(lenBuff);
        ByteConvert::ToObject(lenBuff, cmdlen, LEN_SIZE);
        // 完整包长度
        cmdlen += CMD_STX_LEN;
        cmdlen += LEN_SIZE;
        cmdlen += CMD_ETX_LEN;

        if(IInteractiveTransHelper::ReadAssert(dev, cmdlen, emptyBuffer) != DevHelper::EnumSUCCESS)
            return false;        
        if(pCachedBuff != NULL)
        {
            pCachedBuff->Append(emptyBuffer.SubArray(cmdlen));
        }
        // 剔除多余的数据
        size_t rmvlen = emptyBuffer.GetLength() - cmdlen;
        emptyBuffer.RemoveTail(rmvlen);
        // 10 03 结尾
        if(!StringConvert::EndWith(emptyBuffer, ByteArray(CMD_ETX, CMD_ETX_LEN)))
            return false;
        return true;
    }
    /// 组发送包
    static size_t PackMessage(const ICBC_SendMessage& msg, ByteBuilder& data)
    {
        return PackMessage(msg, msg.Arguments.c_str(), data);
    }
    /// 组发送包
    static size_t PackMessage(const ICBC_SendMessage& msg, const ByteArray& arg, ByteBuilder& data)
    {
        size_t datalen = (40 + 40 + 2 + 8 + 4 + arg.GetLength());

        ByteBuilder lenBuff(4);
        // 设备类型名
        lenBuff = msg.DevId.c_str();
        ByteConvert::Fill(lenBuff, 40, true, 0x00);
        data += lenBuff;
        // 执行动作名
        lenBuff.Clear();
        lenBuff += msg.Action.c_str();
        ByteConvert::Fill(lenBuff, 40, true, 0x00);
        data += lenBuff;
        // 保留
        lenBuff.Clear();
        lenBuff += ArgConvert::ToString(msg.ActionType).c_str();
        ByteConvert::Fill(lenBuff, 2, false, '0');
        data += lenBuff;
        // 超时时间
        lenBuff.Clear();
        lenBuff += ArgConvert::ToString(msg.TimeoutMs).c_str();
        ByteConvert::Fill(lenBuff, 8, false, '0');
        data += lenBuff;
        // 动作序列号
        lenBuff.Clear();
        lenBuff += ArgConvert::ToString(msg.ActionId).c_str();
        ByteConvert::Fill(lenBuff, 4, false, '0');
        data += lenBuff;
        // 参数
        if(arg.GetLength() > 0)
        {
            datalen += 4;
            // 参数长度
            lenBuff.Clear();
            ByteConvert::FromObject<size_t>(arg.GetLength(), lenBuff, 4);
            ByteConvert::Reverse(lenBuff);
            data += lenBuff;
            data += arg;
        }
        return datalen;
    }
    /// 组发送包
    static size_t PackMessage(const ICBC_RecvMessage& msg, ByteBuilder& data)
    {
        return PackMessage(msg, msg.Arguments.c_str(), data);
    }
    /// 组发送包
    static size_t PackMessage(const ICBC_RecvMessage& msg, const ByteArray& arg, ByteBuilder& data)
    {
        size_t datalen = (40 + 40 + 2 + 4 + 4 + arg.GetLength());

        ByteBuilder lenBuff(4);
        // 设备类型名
        lenBuff = msg.DevId.c_str();
        ByteConvert::Fill(lenBuff, 40, true, 0x00);
        data += lenBuff;
        // 执行动作名
        lenBuff.Clear();
        lenBuff += msg.Action.c_str();
        ByteConvert::Fill(lenBuff, 40, true, 0x00);
        data += lenBuff;
        // 保留
        lenBuff.Clear();
        lenBuff += ArgConvert::ToString(msg.ActionType).c_str();
        ByteConvert::Fill(lenBuff, 2, false, '0');
        data += lenBuff;
        // 交易结果
        lenBuff.Clear();
        lenBuff += ArgConvert::ToString(msg.Result).c_str();
        ByteConvert::Fill(lenBuff, 4, false, '0');
        data += lenBuff;
        // 动作序列号
        lenBuff.Clear();
        lenBuff += ArgConvert::ToString(msg.ActionId).c_str();
        ByteConvert::Fill(lenBuff, 4, false, '0');
        data += lenBuff;
        // 参数
        if(arg.GetLength() > 0)
        {
            datalen += 4;
            // 参数长度
            lenBuff.Clear();
            ByteConvert::FromObject<size_t>(arg.GetLength(), lenBuff, 4);
            ByteConvert::Reverse(lenBuff);
            data += lenBuff;
            data += arg;
        }
        return datalen;
    }
    /// 根据指定的数据组包
    static void PackCMD(const ByteArray& data, ByteBuilder& cmd)
    {
        ByteBuilder lenBuff(32);
        ByteConvert::FromObject<size_t>(data.GetLength(), lenBuff, 4);
        ByteConvert::Reverse(lenBuff);

        DevCommand::FromAscii("10 02", cmd);
        cmd += lenBuff;
        cmd += data;
        DevCommand::FromAscii("10 03", cmd);
    }
    /// 加密数据包
    static bool PackCMD(const ByteArray& key_16, const ByteArray& data, ByteBuilder& cmd)
    {
        ByteBuilder buff(128);
        buff = data;
        ByteConvert::FillN(buff, 0, 16, 0x00);

        ByteBuilder lenBuff(32);
        ByteConvert::FromObject<size_t>(buff.GetLength(), lenBuff, 4);
        ByteConvert::Reverse(lenBuff);

        DevCommand::FromAscii("10 02", cmd);
        cmd += lenBuff;
        if(!SM4_Provider::ECB_Encrypt(key_16, buff, cmd))
            return false;
        
        DevCommand::FromAscii("10 03", cmd);
        return true;
    }
    /// 组数据包
    static void PackCMD(const ICBC_SendMessage& msg, ByteBuilder& cmd)
    {
        DevCommand::FromAscii("10 02", cmd);
        cmd.Append(static_cast<byte>(0x00), 4);

        ByteBuilder lenBuff(8);
        size_t len = PackMessage(msg, cmd);

        ByteConvert::FromObject<size_t>(len, lenBuff, 4);
        ByteConvert::Reverse(lenBuff);
        byte* pBuff = const_cast<byte*>(cmd.GetBuffer());
        memcpy(pBuff + 2, lenBuff.GetBuffer(), 4);
        DevCommand::FromAscii("10 03", cmd);
    }
    /// 组数据包
    static void PackCMD(const ICBC_RecvMessage& msg, ByteBuilder& cmd)
    {
        DevCommand::FromAscii("10 02", cmd);
        cmd.Append(static_cast<byte>(0x00), 4);

        ByteBuilder lenBuff(8);
        size_t len = PackMessage(msg, cmd);

        ByteConvert::FromObject<size_t>(len, lenBuff);
        ByteConvert::Reverse(lenBuff);
        byte* pBuff = const_cast<byte*>(cmd.GetBuffer());
        memcpy(pBuff + 2, lenBuff.GetBuffer(), 4);
        DevCommand::FromAscii("10 03", cmd);
    }
    /// 拆包
    static DeviceError::ErrorCode UnpackMessage(const ByteArray& data, ICBC_SendMessage& msg, ByteBuilder* pArg = NULL)
    {
        const size_t CMD_MIN_LEN = (40 + 40 + 2 + 8 + 4);
        if(data.GetLength() < CMD_MIN_LEN)
            return DeviceError::ArgLengthErr;

        ByteBuilder lenBuff(8);
        size_t offset = 0;
        // 设备类型名称
        size_t len = 40;
        lenBuff = data.SubArray(offset, len);
        offset += len;
        msg.DevId = lenBuff.GetString();
        // 动作名称
        lenBuff.Clear();
        len = 40;
        lenBuff = data.SubArray(offset, len);
        offset += len;
        msg.Action = lenBuff.GetString();
        // 动作类型
        lenBuff.Clear();
        len = 2;
        lenBuff = data.SubArray(offset, len);
        offset += len;
        msg.ActionType = ArgConvert::FromString<uint>(lenBuff.GetString());
        // 超时时间
        lenBuff.Clear();
        len = 8;
        lenBuff = data.SubArray(offset, len);
        offset += len;
        msg.TimeoutMs = ArgConvert::FromString<uint>(lenBuff.GetString());
        // 动作序列号
        lenBuff.Clear();
        len = 4;
        lenBuff = data.SubArray(offset, len);
        offset += len;
        msg.ActionId = ArgConvert::FromString<uint>(lenBuff.GetString());
        // 判断是否有参数存在
        if(offset >= data.GetLength())
        {
            msg.Arguments = "";
            return DeviceError::Success;
        }
        // 参数长度
        lenBuff.Clear();
        len = 4;
        lenBuff = data.SubArray(offset, len);
        ByteConvert::Reverse(lenBuff);
        offset += len;
        ByteConvert::ToObject<size_t>(lenBuff, len);
        // 参数
        lenBuff.Clear();
        lenBuff = data.SubArray(offset, len);
        msg.Arguments = lenBuff.GetString();
        if(pArg != NULL)
        {
            pArg->Append(lenBuff);
        }
        return DeviceError::Success;
    }
    /// 拆包
    static DeviceError::ErrorCode UnpackMessage(const ByteArray& data, ICBC_RecvMessage& msg, ByteBuilder* pArg = NULL)
    {
        const size_t CMD_MIN_LEN = (40 + 40 + 2 + 4 + 4);
        if(data.GetLength() < CMD_MIN_LEN)
            return DeviceError::ArgLengthErr;

        ByteBuilder lenBuff(8);
        size_t offset = 0;
        // 设备类型名称
        size_t len = 40;
        lenBuff = data.SubArray(offset, len);
        offset += len;
        msg.DevId = lenBuff.GetString();
        // 动作名称
        lenBuff.Clear();
        len = 40;
        lenBuff = data.SubArray(offset, len);
        offset += len;
        msg.Action = lenBuff.GetString();
        // 动作类型
        lenBuff.Clear();
        len = 2;
        lenBuff = data.SubArray(offset, len);
        offset += len;
        msg.ActionType = ArgConvert::FromString<uint>(lenBuff.GetString());
        // 超时时间
        lenBuff.Clear();
        len = 4;
        lenBuff = data.SubArray(offset, len);
        offset += len;
        msg.Result = ArgConvert::FromString<uint>(lenBuff.GetString());
        // 动作序列号
        lenBuff.Clear();
        len = 4;
        lenBuff = data.SubArray(offset, len);
        offset += len;
        msg.ActionId = ArgConvert::FromString<uint>(lenBuff.GetString());
        // 判断是否有参数存在
        if(offset >= data.GetLength())
        {
            msg.Arguments = "";
            return DeviceError::Success;
        }
        // 参数长度
        lenBuff.Clear();
        len = 4;
        lenBuff = data.SubArray(offset, len);
        ByteConvert::Reverse(lenBuff);
        offset += len;
        ByteConvert::ToObject<size_t>(lenBuff, len);
        // 参数
        lenBuff.Clear();
        lenBuff = data.SubArray(offset, len);
        msg.Arguments = lenBuff.GetString();
        if(pArg != NULL)
        {
            pArg->Append(lenBuff);
        }
        return DeviceError::Success;
    }
    /// 根据指定的数据拆包
    static DeviceError::ErrorCode UnpackCMD(const ByteArray& cmd, size_t& start, size_t& datalen)
    {
        const size_t CMD_MIN_LEN = (2 + 4 + 2);
        if(cmd.GetLength() < CMD_MIN_LEN)
            return DeviceError::ArgLengthErr;

        byte CMD_STX[] = { 0x10, 0x02 };
        const size_t CMD_STX_LEN = 2;

        if(!StringConvert::StartWith(cmd, ByteArray(CMD_STX, CMD_STX_LEN)))
            return DeviceError::ArgFormatErr;
        // 取长度字节
        ByteBuilder lenBuff(8);
        lenBuff = cmd.SubArray(2, 4);
        ByteConvert::Reverse(lenBuff);
        size_t len = 0;
        ByteConvert::ToObject(lenBuff, len);

        if((len + CMD_MIN_LEN) > cmd.GetLength())
            return DeviceError::ArgRangeErr;
        start = 6;
        datalen = len;
        return DeviceError::Success;
    }
    /// 根据指定的数据拆包
    static DeviceError::ErrorCode UnpackCMD(const ByteArray& cmd, ByteBuilder& data)
    {
        size_t start = 0;
        size_t len = 0;
        DeviceError::ErrorCode err = UnpackCMD(cmd, start, len);
        if(DeviceError::IsSuccess(err))
        {
            data = cmd.SubArray(start, len);
        }
        return err;
    }
    /// 解密数据包
    static DeviceError::ErrorCode UnpackCMD(const ByteArray& key_16, const ByteArray& cmd, ByteBuilder& data)
    {
        size_t start = 0;
        size_t len = 0;
        DeviceError::ErrorCode err = UnpackCMD(cmd, start, len);
        if(err != DeviceError::Success)
            return err;

        if(!SM4_Provider::ECB_Decrypt(key_16, cmd.SubArray(start, len), data))
            err = DeviceError::OperatorStatusErr;
        
        return DeviceError::Success;
    }
};
//--------------------------------------------------------- 
} // namespace icbc
} // namespace application 
} // namespace zhou_yb 
//--------------------------------------------------------- 
#endif // _LIBZHOUYB_ICBC_CMDHELPER_H_
//========================================================= 